#include <stdio.h>
#include <stdlib.h>
#include <acev/rcu.h>

// Adresse des DMA-Basisregisters
#define REG_DMA_BASE   0x3BFFFFF

// Anzahl Datensaetze in Ein- und Ausgabedatei
#define NUM_WORDS 256*64

// Definitionen fuer RC Register
#define REG_SOURCE_ADDR   0
#define REG_DEST_ADDR     1
#define REG_COUNT         2
#define REG_START         3

// Hauptprogramm
main(
     int argc,     // Anzahl Kommandozeilenparameter + Programmname
     char *argv[]  // Programmname + Kommandozeilenparameter als Strings
     )
{
  // Zeiger auf Adressraum mit RC-Registern
  unsigned long volatile *rc;

  // Benutze vorzeichenlose Ganzzahlen fuer alle Variablen
  unsigned long volatile *inwords, *outwords,
    *inwords_phys, *outwords_phys;
  // Marker fuer Zeitmessung
  unsigned long long start, stop, rcu_get_ticks();

  // Weitere Variablendefinitionen

  // Eingabe- und Ausgabedatei
  FILE *infile, *outfile;

  // Eingabedatei oeffnen
  if ((infile = fopen(argv[1], "r")) == NULL) {
    printf("Eingabedatei %s konnte nicht geoeffnet werden.\n", argv[1]);
    exit(2);
  }

  // Ausgabedatei oeffnen
  if ((outfile = fopen(argv[2], "w")) == NULL) {
    printf("Ausgabedatei %s konnte nicht geoeffnet werden.\n", argv[2]);
    exit(3);
  } 

  // RC initialisieren
  rcu_init();
  // Zeiger auf Adressraum mit RC-Registern holen
  rc = rcu_get_s0(NULL);

  // fordere Speicher fuer Ein- und Ausgabearrays an
  // *_phys zeigt auf die physikalische Speicheradresse
  // aus Sicht der Hardware
  inwords = rcu_malloc_master(2 * NUM_WORDS * sizeof(unsigned long),
			      (void **) &inwords_phys);
  outwords      = inwords      + NUM_WORDS;
  outwords_phys = inwords_phys + NUM_WORDS;

  // Setze DMA-Basisregister auf die naechstniedrigere 16MB-Grenze
  rc[REG_DMA_BASE] = (unsigned long) inwords_phys & 0xFF000000;

  // Funktioniert der Slave Zugriff?
  printf ("Magic: %.8lx\n", rc[28]);

  if (!inwords || !inwords_phys) {
    fprintf(stderr, "out of memory\n");
    exit(1);
  }

  // Einlesen von Eingabedaten
  // Reiche PGM Kopfdatensatz direkt durch
  fgets((void*)inwords, 80, infile);
  fputs((void*)inwords,  outfile);
  fgets((void*)inwords, 80, infile);
  fputs((void*)inwords,  outfile);
  fgets((void*)inwords, 80, infile);
  fputs((void*)inwords,  outfile);

  // lese gesamtes Feld von Bildpunkten auf einen Satz
  fread(inwords, sizeof(unsigned char), 256*256, infile);

  // Merke Startzeit der Berechnung
  start = rcu_get_ticks();

  // Uebertrage Parameter an RC (nicht die Daten selbst)
  rc[REG_SOURCE_ADDR] = inwords_phys;  // physikalische(!) startaddresse
  rc[REG_DEST_ADDR]   = outwords_phys; // physikalische(!) zieladdresse
  rc[REG_COUNT]       = NUM_WORDS;     // anzahl datenworte

  rc[REG_START] = 1;                // startkommando

  // Warte auf Ende der Berechnung (wird ueber IRQ angezeigt)
  rcu_wait();

  // merke Stopzeit
  stop = rcu_get_ticks();

  // Hardware Interrupt zuruecksetzen durch beliebigen HW Lesezugriff
  rc[28];

  // gebe gesamtes Feld von Bildpunkten auf einen Satz aus
  fwrite(outwords, sizeof(unsigned char), 256*256, outfile);

  // Gebe Speicher fuer Ein-/Ausgabe-Arrays  wieder frei
  rcu_free_master((void *) inwords);

  // Dateien schliessen
  fclose(infile);
  fclose(outfile);

 // Funktioniert der Slave Zugriff?
  printf ("Magic: %.8lx\n", rc[28]);

  // Gebe Ergebnis der Zeitmessung in Mikrosekunden aus
  printf("NUM_WORDS=%d\n", NUM_WORDS);
  printf("Zeit  : %8lld us\n", (stop-start)/TICKS_PER_USEC);
}
